package com.bear.atlassianmcp.oauth.strategy;

import com.bear.atlassianmcp.common.util.SessionUtil;
import com.bear.atlassianmcp.oauth.config.OAuthProperties;
import com.bear.atlassianmcp.oauth.context.OAuthContextHolder;
import com.bear.atlassianmcp.oauth.model.OAuthRequestContext;
import com.bear.atlassianmcp.oauth.model.OAuthToken;
import com.bear.atlassianmcp.oauth.service.OAuthService;
import com.bear.atlassianmcp.oauth.util.OAuthSignatureUtil;
import com.bear.atlassianmcp.oauth.util.OAuthTokenParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * OAuth 1.0a 认证策略实现
 * 支持 Jira 和 Confluence 的 OAuth 1.0a 认证
 */
@Component
public class OAuthStrategy implements AuthenticationStrategy {
    
    private static final Logger logger = LoggerFactory.getLogger(OAuthStrategy.class);
    
    private final OAuthProperties oauthProperties;
    private final OAuthService oauthService;
    private final OAuthSignatureUtil signatureUtil;
    private final OAuthTokenParser tokenParser;
    private final RestTemplate oauthRestTemplate;

    @Autowired
    public OAuthStrategy(OAuthProperties oauthProperties, OAuthService oauthService,
                        OAuthSignatureUtil signatureUtil, OAuthTokenParser tokenParser,
                        RestTemplate oauthRestTemplate) {
        this.oauthProperties = oauthProperties;
        this.oauthService = oauthService;
        this.signatureUtil = signatureUtil;
        this.tokenParser = tokenParser;
        this.oauthRestTemplate = oauthRestTemplate;
    }
    
    @Override
    public HttpHeaders createAuthHeaders(ToolContext toolContext) {
        HttpHeaders headers = new HttpHeaders();

        // 设置Content-Type
        headers.set("Content-Type", "application/json");
        headers.set("Accept", "application/json");

        // 从请求头中获取 OAuth 令牌
        OAuthToken accessToken = getTokenFromRequest(toolContext);
        if (accessToken == null) {
            throw new RuntimeException("No valid OAuth token found in request headers. " +
                                     "Please provide OAuth token in format: " + tokenParser.getExampleFormat());
        }

        // 从令牌中确定服务类型
        ServiceType serviceType = convertServiceType(accessToken.getServiceType());

        // 生成 OAuth 授权头
        String authHeader = generateOAuthHeader(toolContext, accessToken, serviceType);
        headers.set("Authorization", authHeader);

        logger.debug("Using OAuth 1.0a authorization for {} with token from request", serviceType);
        return headers;
    }
    
    @Override
    public void validateAuthConfiguration() {
        // 验证 Jira OAuth 配置
        if (oauthProperties.getJira().isEnabled()) {
            validateServiceConfig(ServiceType.JIRA);
        }
        
        // 验证 Confluence OAuth 配置
        if (oauthProperties.getConfluence().isEnabled()) {
            validateServiceConfig(ServiceType.CONFLUENCE);
        }
        
        logger.info("OAuth configuration validation completed successfully");
    }
    
    @Override
    public AuthenticationType getAuthenticationType() {
        return AuthenticationType.OAUTH_1_0A;
    }
    
    @Override
    public boolean supportsServiceType(ServiceType serviceType) {
        switch (serviceType) {
            case JIRA:
                return oauthProperties.getJira().isEnabled();
            case CONFLUENCE:
                return oauthProperties.getConfluence().isEnabled();
            default:
                return false;
        }
    }
    
    @Override
    public String testConnection(ToolContext toolContext, String baseUrl) {
        logger.debug("Testing OAuth connection to {}", baseUrl);

        try {
            // 从请求头中获取 OAuth 令牌
            OAuthToken accessToken = getTokenFromRequest(toolContext);
            if (accessToken == null) {
                return "OAuth connection test failed: No valid OAuth token found in request headers. " +
                       "Please provide OAuth token in format: " + tokenParser.getExampleFormat();
            }

            ServiceType serviceType = convertServiceType(accessToken.getServiceType());

            // 构建测试URL
            String testUrl = baseUrl + "/rest/api/2/myself";

            // 生成 OAuth 授权头
            String authHeader = signatureUtil.generateAuthorizationHeader(
                "GET", testUrl,
                getConsumerKey(serviceType),
                accessToken.getToken(),
                accessToken.getTokenSecret(),
                getPrivateKey(serviceType)
            );

            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", authHeader);
            headers.set("Accept", "application/json");

            HttpEntity<String> entity = new HttpEntity<>(headers);

            ResponseEntity<String> response = oauthRestTemplate.exchange(
                testUrl, HttpMethod.GET, entity, String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                return "OAuth 1.0a connection successful for " + serviceType + " using token from request!";
            } else {
                return "OAuth connection failed with status: " + response.getStatusCode();
            }

        } catch (Exception e) {
            logger.error("OAuth connection test failed: {}", e.getMessage());
            return "OAuth connection test failed: " + e.getMessage();
        }
    }
    
    /**
     * 验证服务配置
     */
    private void validateServiceConfig(ServiceType serviceType) {
        String consumerKey = getConsumerKey(serviceType);
        String privateKey = getPrivateKey(serviceType);
        String baseUrl = getBaseUrl(serviceType);
        
        if (consumerKey == null || consumerKey.trim().isEmpty()) {
            throw new RuntimeException(serviceType + " OAuth consumer key must be configured");
        }
        
        if (privateKey == null || privateKey.trim().isEmpty()) {
            throw new RuntimeException(serviceType + " OAuth private key must be configured");
        }
        
        if (baseUrl == null || baseUrl.trim().isEmpty()) {
            throw new RuntimeException(serviceType + " base URL must be configured");
        }
        
        // 验证私钥格式
        if (!signatureUtil.validatePrivateKey(privateKey)) {
            throw new RuntimeException(serviceType + " OAuth private key format is invalid");
        }
        
        logger.info("{} OAuth configuration validated successfully", serviceType);
    }
    
    /**
     * 从请求头中获取 OAuth 令牌
     */
    private OAuthToken getTokenFromRequest(ToolContext toolContext) {
        try {
            String authorization = SessionUtil.getAuthorization(toolContext);
            if (authorization == null) {
                logger.debug("No authorization header found in request");
                return null;
            }

            if (!authorization.startsWith("OAuth ")) {
                logger.debug("Authorization header is not OAuth format");
                return null;
            }

            OAuthTokenParser.ParseResult result = tokenParser.parseWithValidation(authorization);
            if (!result.isSuccess()) {
                logger.warn("Failed to parse OAuth token from request: {}", result.getError());
                return null;
            }

            logger.debug("Successfully parsed OAuth token from request for service: {}",
                        result.getToken().getServiceType());
            return result.getToken();

        } catch (Exception e) {
            logger.error("Error getting OAuth token from request: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 转换服务类型
     */
    private ServiceType convertServiceType(OAuthToken.ServiceType tokenServiceType) {
        return tokenServiceType == OAuthToken.ServiceType.JIRA ?
               ServiceType.JIRA : ServiceType.CONFLUENCE;
    }

    /**
     * 获取或创建 Access Token（保留用于授权流程）
     * @deprecated 现在使用请求头中的令牌
     */
    @Deprecated
    private OAuthToken getOrCreateAccessToken(ServiceType serviceType) {
        // 转换服务类型
        OAuthToken.ServiceType tokenServiceType = serviceType == ServiceType.JIRA ?
            OAuthToken.ServiceType.JIRA : OAuthToken.ServiceType.CONFLUENCE;

        // 尝试加载现有的 Access Token
        OAuthToken existingToken = oauthService.loadToken(tokenServiceType);
        if (existingToken != null && existingToken.isValidAccessToken()) {
            logger.debug("Using existing valid access token for {}", serviceType);
            return existingToken;
        }

        // 如果没有有效的 Access Token，启动授权流程
        logger.info("No valid access token found for {}, starting authorization flow", serviceType);
        return oauthService.startAuthorizationFlow(tokenServiceType);
    }
    
    /**
     * 生成 OAuth 授权头
     */
    private String generateOAuthHeader(ToolContext toolContext, OAuthToken accessToken, ServiceType serviceType) {
        // 从工具上下文中获取请求URL（这里简化处理，实际应用中可能需要更复杂的逻辑）
        String requestUrl = extractRequestUrl(toolContext, serviceType);
        String httpMethod = extractHttpMethod(toolContext);
        
        return signatureUtil.generateAuthorizationHeader(
            httpMethod, requestUrl,
            getConsumerKey(serviceType),
            accessToken.getToken(),
            accessToken.getTokenSecret(),
            getPrivateKey(serviceType)
        );
    }
    
    /**
     * 从工具上下文中确定服务类型
     * 优先从 OAuthContextHolder 获取，然后尝试从 ToolContext 推断
     */
    private ServiceType determineServiceType(ToolContext toolContext) {
        logger.debug("Determining service type from context");

        // 1. 优先从 OAuthContextHolder 获取服务类型
        if (OAuthContextHolder.hasContext()) {
            ServiceType contextServiceType = OAuthContextHolder.getServiceType();
            if (contextServiceType != null) {
                logger.debug("Service type from OAuth context: {}", contextServiceType);
                return contextServiceType;
            }

            // 如果上下文存在但没有明确的服务类型，尝试从 URL 推断
            ServiceType inferredType = OAuthContextHolder.inferServiceType(null);
            if (inferredType != null) {
                logger.debug("Inferred service type from OAuth context URL: {}", inferredType);
                return inferredType;
            }
        }

        // 2. 尝试从 ToolContext 的字符串表示中推断
        if (toolContext != null) {
            String contextInfo = toolContext.toString().toLowerCase();
            logger.debug("Analyzing ToolContext string: {}", contextInfo.substring(0, Math.min(contextInfo.length(), 100)));

            // 检查是否包含 Confluence 相关关键词
            if (contextInfo.contains("confluence") ||
                contextInfo.contains("/wiki/") ||
                contextInfo.contains("confluenceservice") ||
                contextInfo.contains("confluenceauth")) {
                logger.debug("Detected Confluence from ToolContext");
                return ServiceType.CONFLUENCE;
            }

            // 检查是否包含 Jira 相关关键词
            if (contextInfo.contains("jira") ||
                contextInfo.contains("jiraservice") ||
                contextInfo.contains("jiraauth") ||
                contextInfo.contains("/rest/api/")) {
                logger.debug("Detected Jira from ToolContext");
                return ServiceType.JIRA;
            }
        }

        // 3. 尝试从调用栈分析服务类型
        ServiceType stackServiceType = determineServiceTypeFromStack();
        if (stackServiceType != null) {
            logger.debug("Determined service type from call stack: {}", stackServiceType);
            return stackServiceType;
        }

        // 4. 默认返回 JIRA
        logger.debug("Using default service type: JIRA");
        return ServiceType.JIRA;
    }

    /**
     * 从调用栈中分析服务类型
     */
    private ServiceType determineServiceTypeFromStack() {
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

            for (StackTraceElement element : stackTrace) {
                String className = element.getClassName().toLowerCase();
                String methodName = element.getMethodName().toLowerCase();

                // 检查类名中的服务类型指示
                if (className.contains("confluence")) {
                    return ServiceType.CONFLUENCE;
                } else if (className.contains("jira")) {
                    return ServiceType.JIRA;
                }

                // 检查方法名中的服务类型指示
                if (methodName.contains("confluence")) {
                    return ServiceType.CONFLUENCE;
                } else if (methodName.contains("jira")) {
                    return ServiceType.JIRA;
                }
            }
        } catch (Exception e) {
            logger.debug("Failed to analyze call stack for service type: {}", e.getMessage());
        }

        return null;
    }
    
    /**
     * 从工具上下文中提取请求URL
     * 优先从 OAuthContextHolder 获取，然后构建默认 URL
     */
    private String extractRequestUrl(ToolContext toolContext, ServiceType serviceType) {
        logger.debug("Extracting request URL for service type: {}", serviceType);

        // 1. 优先从 OAuthContextHolder 获取请求 URL
        if (OAuthContextHolder.hasContext()) {
            String contextUrl = OAuthContextHolder.getRequestUrl();
            if (contextUrl != null && !contextUrl.trim().isEmpty()) {
                logger.debug("Using request URL from OAuth context: {}", contextUrl);
                return contextUrl;
            }
        }

        // 2. 尝试从 ToolContext 中推断请求信息
        String inferredUrl = inferUrlFromToolContext(toolContext, serviceType);
        if (inferredUrl != null) {
            logger.debug("Inferred request URL from ToolContext: {}", inferredUrl);
            return inferredUrl;
        }

        // 3. 构建默认的 API URL
        String baseUrl = getBaseUrl(serviceType);
        String defaultUrl;

        if (serviceType == ServiceType.CONFLUENCE) {
            // Confluence 默认使用 /rest/api/content 端点
            defaultUrl = baseUrl + "/rest/api/content";
        } else {
            // Jira 默认使用 /rest/api/2/myself 端点（用于测试连接）
            defaultUrl = baseUrl + "/rest/api/2/myself";
        }

        logger.debug("Using default request URL: {}", defaultUrl);
        return defaultUrl;
    }

    /**
     * 从 ToolContext 中推断请求 URL
     */
    private String inferUrlFromToolContext(ToolContext toolContext, ServiceType serviceType) {
        if (toolContext == null) {
            return null;
        }

        try {
            // 尝试从 ToolContext 的字符串表示中提取 URL 信息
            String contextString = toolContext.toString();

            // 查找可能的 URL 模式
            String[] urlPatterns = {
                "/rest/api/",
                "/rest/",
                "/api/",
                "/browse/",
                "/wiki/"
            };

            String baseUrl = getBaseUrl(serviceType);

            for (String pattern : urlPatterns) {
                int patternIndex = contextString.indexOf(pattern);
                if (patternIndex != -1) {
                    // 尝试提取完整的路径
                    int startIndex = contextString.lastIndexOf(' ', patternIndex);
                    int endIndex = contextString.indexOf(' ', patternIndex);

                    if (startIndex == -1) startIndex = 0;
                    if (endIndex == -1) endIndex = contextString.length();

                    String extractedPath = contextString.substring(startIndex, endIndex).trim();

                    // 如果提取的路径看起来像一个有效的 API 路径
                    if (extractedPath.startsWith("/") && extractedPath.length() > pattern.length()) {
                        String fullUrl = baseUrl + extractedPath;
                        logger.debug("Extracted URL from ToolContext: {}", fullUrl);
                        return fullUrl;
                    }
                }
            }

            // 尝试查找特定的 API 端点模式
            if (serviceType == ServiceType.JIRA) {
                if (contextString.contains("issue") || contextString.contains("search")) {
                    return baseUrl + "/rest/api/2/search";
                } else if (contextString.contains("project")) {
                    return baseUrl + "/rest/api/2/project";
                } else if (contextString.contains("user") || contextString.contains("myself")) {
                    return baseUrl + "/rest/api/2/myself";
                }
            } else if (serviceType == ServiceType.CONFLUENCE) {
                if (contextString.contains("content") || contextString.contains("page")) {
                    return baseUrl + "/rest/api/content";
                } else if (contextString.contains("space")) {
                    return baseUrl + "/rest/api/space";
                }
            }

        } catch (Exception e) {
            logger.debug("Failed to infer URL from ToolContext: {}", e.getMessage());
        }

        return null;
    }
    
    /**
     * 从工具上下文中提取HTTP方法
     * 优先从 OAuthContextHolder 获取，然后从上下文推断
     */
    private String extractHttpMethod(ToolContext toolContext) {
        logger.debug("Extracting HTTP method from context");

        // 1. 优先从 OAuthContextHolder 获取 HTTP 方法
        if (OAuthContextHolder.hasContext()) {
            String contextMethod = OAuthContextHolder.getHttpMethod();
            if (contextMethod != null && !contextMethod.trim().isEmpty()) {
                logger.debug("Using HTTP method from OAuth context: {}", contextMethod);
                return contextMethod.toUpperCase();
            }
        }

        // 2. 尝试从 ToolContext 中推断 HTTP 方法
        String inferredMethod = inferHttpMethodFromToolContext(toolContext);
        if (inferredMethod != null) {
            logger.debug("Inferred HTTP method from ToolContext: {}", inferredMethod);
            return inferredMethod;
        }

        // 3. 从调用栈中推断 HTTP 方法
        String stackMethod = inferHttpMethodFromStack();
        if (stackMethod != null) {
            logger.debug("Inferred HTTP method from call stack: {}", stackMethod);
            return stackMethod;
        }

        // 4. 默认返回 GET
        logger.debug("Using default HTTP method: GET");
        return "GET";
    }

    /**
     * 从 ToolContext 中推断 HTTP 方法
     */
    private String inferHttpMethodFromToolContext(ToolContext toolContext) {
        if (toolContext == null) {
            return null;
        }

        try {
            String contextString = toolContext.toString().toLowerCase();

            // 查找明确的 HTTP 方法指示
            if (contextString.contains("post") || contextString.contains("create") ||
                contextString.contains("add") || contextString.contains("insert")) {
                return "POST";
            } else if (contextString.contains("put") || contextString.contains("update") ||
                      contextString.contains("modify") || contextString.contains("edit")) {
                return "PUT";
            } else if (contextString.contains("delete") || contextString.contains("remove")) {
                return "DELETE";
            } else if (contextString.contains("patch")) {
                return "PATCH";
            } else if (contextString.contains("get") || contextString.contains("search") ||
                      contextString.contains("find") || contextString.contains("query") ||
                      contextString.contains("list") || contextString.contains("retrieve")) {
                return "GET";
            }

            // 根据操作类型推断
            if (contextString.contains("issue") && contextString.contains("create")) {
                return "POST";
            } else if (contextString.contains("comment") && contextString.contains("add")) {
                return "POST";
            } else if (contextString.contains("attachment") && contextString.contains("upload")) {
                return "POST";
            }

        } catch (Exception e) {
            logger.debug("Failed to infer HTTP method from ToolContext: {}", e.getMessage());
        }

        return null;
    }

    /**
     * 从调用栈中推断 HTTP 方法
     */
    private String inferHttpMethodFromStack() {
        try {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

            for (StackTraceElement element : stackTrace) {
                String methodName = element.getMethodName().toLowerCase();

                // 根据方法名推断 HTTP 方法
                if (methodName.contains("create") || methodName.contains("add") ||
                    methodName.contains("post") || methodName.contains("insert")) {
                    return "POST";
                } else if (methodName.contains("update") || methodName.contains("put") ||
                          methodName.contains("modify") || methodName.contains("edit")) {
                    return "PUT";
                } else if (methodName.contains("delete") || methodName.contains("remove")) {
                    return "DELETE";
                } else if (methodName.contains("patch")) {
                    return "PATCH";
                } else if (methodName.contains("get") || methodName.contains("search") ||
                          methodName.contains("find") || methodName.contains("query") ||
                          methodName.contains("list") || methodName.contains("retrieve")) {
                    return "GET";
                }
            }
        } catch (Exception e) {
            logger.debug("Failed to infer HTTP method from call stack: {}", e.getMessage());
        }

        return null;
    }
    
    // 辅助方法
    private String getConsumerKey(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getConsumerKey() : 
               oauthProperties.getConfluence().getConsumerKey();
    }
    
    private String getPrivateKey(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getPrivateKey() : 
               oauthProperties.getConfluence().getPrivateKey();
    }
    
    private String getBaseUrl(ServiceType serviceType) {
        return serviceType == ServiceType.JIRA ? 
               oauthProperties.getJira().getBaseUrl() : 
               oauthProperties.getConfluence().getBaseUrl();
    }
    
    /**
     * 强制重新授权
     */
    public OAuthToken forceReauthorization(ServiceType serviceType) {
        OAuthToken.ServiceType tokenServiceType = serviceType == ServiceType.JIRA ? 
            OAuthToken.ServiceType.JIRA : OAuthToken.ServiceType.CONFLUENCE;
        
        logger.info("Forcing reauthorization for {}", serviceType);
        return oauthService.startAuthorizationFlow(tokenServiceType);
    }
    
    /**
     * 检查是否有有效的访问令牌（从请求头中）
     */
    public boolean hasValidAccessToken(ServiceType serviceType) {
        // 注意：这个方法现在需要 ToolContext 来检查请求头中的令牌
        // 但为了保持接口兼容性，我们返回 false 并记录警告
        logger.warn("hasValidAccessToken called without ToolContext - cannot check request headers");
        return false;
    }

    /**
     * 检查是否有有效的访问令牌（从请求头中）
     */
    public boolean hasValidAccessToken(ServiceType serviceType, ToolContext toolContext) {
        try {
            OAuthToken token = getTokenFromRequest(toolContext);
            if (token == null) {
                return false;
            }

            // 检查服务类型是否匹配
            ServiceType tokenServiceType = convertServiceType(token.getServiceType());
            if (tokenServiceType != serviceType) {
                logger.debug("Service type mismatch: expected {}, got {}", serviceType, tokenServiceType);
                return false;
            }

            return token.isValidAccessToken();
        } catch (Exception e) {
            logger.debug("Error checking access token validity: {}", e.getMessage());
            return false;
        }
    }
}
